
// ===============================================================================================================
// -*- C++ -*-
//
// GfxLib.hpp - Graphics rendering code.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __GFXLIB_HPP__
#define __GFXLIB_HPP__

#include <CommLib.hpp>
#include <MathLib.hpp>
#include <INIFile.hpp>
#include <vector>

#if defined (_WIN32)
#include <windows.h>
#include <glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif // _WIN32

// Forward decl
namespace MeshLib { class Mesh; };

namespace GfxLib {

// =========================================================
// GFX Setup
// =========================================================

// Initializes the GFX library. Must be called once, before using the module for the first time.
bool Initialize(const char * cfgFileName);

// Enable 'numLights' lights in the scene and set its positions using 'lightPositions'.
void EnableLights(int numLights, const MathLib::Vec3f * lightPositions);

// Sets the current perspective projection.
void SetViewPort(int w, int h, float fov, float zNear, float zFar);

// Set the GLUT callbacks. (Param may be null if not needed)
void SetCallbacks(void (*render)(void), void (*reshapeWindow)(int width, int height),
				  void (*mouse)(int button, int state, int x, int y), void (*mouseMotion)(int x, int y),
				  void (*keyboard)(unsigned char key, int x, int y), void (*keyboardUp)(unsigned char key, int x, int y));

// Run the render() callback function in an infinite loop. Call exit() to break it
void RunRenderingLoop(void);

// Cleanup the GFX library before exit.
void Terminate(void);

// Get the number of lights enabled by EnableLights(), or zero if no light enabled.
extern int numEnabledLights;

// Viewport vars:
extern float fov;
extern float zNear;
extern float zFar;

// =========================================================
// Screen Information
// =========================================================

struct ScreenInfo {

	int width;  // Width of the screen in pixels
	int height; // Height of the screen in pixels
};

// Global screen information.
extern ScreenInfo screen;

// =========================================================
// Font/Text Rendering
// =========================================================

// Number of characters in the font (Ascii font 256 chars)
static const int maxFontChars = 256;

// Creates a system font for rendering to the screen. Returns the font id.
int CreateBitmapFont(const char * fontName, int width, int height, bool bold, bool italic, bool underline, bool strikeout);

// Set the text color for ScreenPrintf().
void SetTextColor(unsigned long color);

// Print a format string to the screen using the given font.
void ScreenPrintf(int font, int x, int y, const char * format, ...);

// Destroy all loaded fonts.
void CleanupFonts(void);

// =========================================================
// 2D Textures
// =========================================================

class Texture2D {

public:

	int width, height; // Texture dimensions
	GLuint id;         // OpenGL Texture Id
	GLint wrapMode;    // Wrap mode (GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT)
	GLint minFilter;   // GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR
	GLint magFilter;   // GL_LINEAR, GL_NEAREST
	std::string fileName; // Original file name

	// Support only .bmp and .jpg images for now.
	Texture2D(const char * fileName, GLint wrapMode, GLint minFilter, GLint magFilter);

	// Texture from a memory buffer.
	Texture2D(const unsigned char * pixels, const char * textureName,
	int width, int height, int bytesPerPix, GLenum glTexFmt, GLint wrapMode, GLint minFilter, GLint magFilter);

	// Check if the texture creation failed.
	bool Fail(void) const;

	// Bind the texture to a geometry.
	void Bind(unsigned int texUnit = 0) const;

private:

	// Disable copy and assignment.
	Texture2D(const Texture2D &);
	Texture2D & operator = (const Texture2D &);
};

// Set the maximun number of textures that can be created at once.
void InitTextures(unsigned int maxTextures);

// Creates a OpenGL texture. Keeps it in chache until a CleanupTextures() is called. Support only .bmp and .jpg images for now.
bool CreateGLTexture2DFromFile(const char * fileName, GLint wrapMode, GLint minFilter, GLint magFilter, int & w, int & h, GLuint & id);

// Creates a OpenGL texture from a memory buffer of pixels.
bool CreateGLTexture2DFromMemory(const unsigned char * pixels, const char * textureName, GLint wrapMode,
								 GLint minFilter, GLint magFilter, GLenum glTexFmt, int w, int h, int bytesPerPix, GLuint & id);

// Release all loaded textures.
void CleanupTextures(void);

// =========================================================
// Image Loading
// =========================================================

// Pixel buffer from a windows bitmap image file.
unsigned char * LoadBitmapImage(const char * fileName, int & width, int & height, int & bytesPerPix);

// Pixel buffer from a JPEG image file.
unsigned char * LoadJpegImage(const char * fileName, int & width, int & height, int & bytesPerPix);

// Pixel buffer from an uncompressed Truevision TGA image file.
unsigned char * LoadTgaImage(const char * fileName, int & width, int & height, int & bytesPerPix);

// =========================================================
// Shader Programs
// =========================================================

class ShaderProgram {

public:

	// Interface for a GPU shader program.
	// Supported types are the Fragment Shader (Pixel Shader), Geometry Shader
	// and the Vertex Shader, currently implemented for the OpenGL rendering API.
	// Shader info logs are redirected to the framework Log system by default.

	// Creates a new shader program from the specified source files.
	// The shader type (i.e: Vertex/Geometry/Fragment shader) is detected by checking the source file extension,
	// which must be either ".vert" for vertex shaders, ".frag" for fragment shaders or ".geom" for geometry Shaders.
	ShaderProgram(const char ** srcFiles, size_t numFiles);

	// Creates a shader from a shader source code string.
	// 'shaderType' must be equal to GL_VERTEX_SHADER or GL_FRAGMENT_SHADER or GL_GEOMETRY_SHADER_EXT.
	ShaderProgram(const char ** shaderCodes, size_t count, const int * shaderTypes);

	// Enable the shader program.
	void Enable(void) const;

	// Disable the shader program (return to fixed functionality).
	void Disable(void) const;

	// TODO: Add get/set methods for the different shader data types !!!

	// Set a shader uniform variable.
	void SetUniform1i(const char * name, int i);

	// Set a shader uniform variable.
	void SetUniform1f(const char * name, float f);

	~ShaderProgram(void);

private:

	// Disable copy and assignment.
	ShaderProgram(const ShaderProgram &);
	ShaderProgram & operator = (const ShaderProgram &);

	// Write the program info log to the default log output.
	void WriteInfoLog(void) const;

	GLuint programObj; // Shader program object for OpenGL
	std::vector<GLuint> shaders; // Shader instances in this shader program
};

// Standard blinn-phong shader to use in the OBJs. Supports N lights and 1 texture level.
extern ShaderProgram * defaultShader;

// =========================================================
// Simple Material Class
// =========================================================

struct Material {

	Texture2D * diffuseMap; // Has a texture if NOT null!

	// Material colors as floats:
	MathLib::Vec3f ambientColor;
	MathLib::Vec3f diffuseColor;
	MathLib::Vec3f specularColor;
	float shininess;

	// Default init:
	inline Material(void) : diffuseMap(0),
	ambientColor(0.5f, 0.5f, 0.5f), diffuseColor(0.5f, 0.5f, 0.5f),
	specularColor(0.5f, 0.5f, 0.5f), shininess(15.0f) { /**/ }

	// Memory cleanup:
	inline ~Material(void)
	{
		delete diffuseMap;
	}
};

// Standard material for objects without one.
extern Material * defaultMaterial;

// =========================================================
// 3D Rendering
// =========================================================

enum RenderMode {

	// Render the mesh with filled polygons.
	RENDER_SOLID,
	// Reder the mesh as a cloud of points.
	RENDER_POINTS,
	// Render the mesh in wireframe.
	RENDER_WIREFRAME,
};

// Render a 3D mesh with the current world transform.
void RenderMesh(const MeshLib::Mesh * mesh, RenderMode renderMode);

// Interpolate between 2 meshes and render the resulting mesh with the current world transform.
void InterpolateAndRenderMesh(const MeshLib::Mesh * mesh1, const MeshLib::Mesh * mesh2, float interp, RenderMode renderMode);

}; // namespace GfxLib {}

#endif // __GFXLIB_HPP__